import socket
import json
import sys
import requests
import time
import json
import os

from flashtool_proc import flashProc, checkEspToolProc, eraseFlashProc01, get_local_ip,updartProc,comboneBinProc, verifyNotEncyptedBoard
from dlfile import download, download_and_unzip
from device_status import get_hub_status, update_hub_burn_status
from dcms_client import getHubInfo, getFwBinFileName, SERVER_HOST, TOKEN, FILESERVER_HOST, AGENT_NAME

#DEBUG_MODE = True
DEBUG_MODE = False
FIRMWARE_PATH = "./working/firmware/"
HUB_CFG_PATH = "./working/cfg/"
TMP_PATH = "./working/tmp/"
KEY_FILE_PATH = "./working/"
COM_PORT = "COM4"

def flashBinIntoModule(command_dict, conn):
    if 'modid' not in command_dict:
        return {"cmdAck": "flash", "reqId": command_dict.get("reqId"), "code": "910", "msg": "Invalid parameters: modid is required"}

    if 'hubId' not in command_dict:
        return {"cmdAck": "flash", "reqId": command_dict.get("reqId"), "code": "910", "msg": "Invalid parameters: hubId is required"}

    if DEBUG_MODE:
        return {"cmdAck": "flash", "reqId": command_dict.get("reqId"), "code": "200", "msg": "success"}

    #getHubInfo
    headers = {'token': TOKEN}
    data = {'module_id': command_dict['modid'], 'user_name': AGENT_NAME, 'prod_code': command_dict['prodCode']}
    url = SERVER_HOST+"getHubInfoByModuleId"

    api_res = requests.post(url, headers=headers, json=data)    
    if api_res.status_code == 200:
        api_res_json = api_res.json()
        print(api_res_json)
        #checkModuleStatus and hubData
        if (api_res_json['code'] == '200'):
            if (api_res_json['module']['status']=='F'):
                #downloadBin
                fw_bin_file = getFwBinFileName(api_res_json['module']['prod_code'],'')
                if(fw_bin_file is None):
                    return {"cmdAck": "flash", "reqId": command_dict.get("reqId"), "code": "951", "msg": "Failed to get fw bin file name"}

                print(fw_bin_file)

                combined_BinFileName = fw_bin_file['file'].replace("key_", "Combine_")  
                print("key_AppBinFileName = %s need combined_file = %s"%(fw_bin_file['file'],combined_BinFileName))
                # 视情况而定，是否要将 下述烧录 combine bin中 fw_bin_file['combined_file'] 参数替换成 combined_BinFileName
                input("get the bin name,fw_bin_file['combined_file'] = %s and the combined_BinFileName = %s  merge bin Press Enter to continue..."%(fw_bin_file['combined_file'],combined_BinFileName))
                # 这里判断是否有combine bin 文件
                if not os.path.exists(FIRMWARE_PATH+combined_BinFileName) :
                    # 下载 combine 固件改成下载 key_APP 固件 
                    download(FILESERVER_HOST,fw_bin_file['file'], FIRMWARE_PATH)
                    # 下载固件后 合并成 combine bin
                    result = comboneBinProc(command_dict,FIRMWARE_PATH,fw_bin_file['file'],DEBUG_MODE)
                    if(result['code'] != '200'):
                        return result
                    print("downloaded bin file and combine successful!")
                    # input("after comboneBinProc Press Enter to continue...")
                    #download bin file
                    #print("downloaded bin file!")
                input("after bin merge and now download hubid Press Enter to continue...")
                #input("Press Enter to continue...")
                CFG_FILE_DIR = HUB_CFG_PATH + api_res_json['module']['hub_id']
                if not os.path.exists(CFG_FILE_DIR):                    
                    config_bin_file_name = api_res_json['module']['hub_id'] + ".zip"
                    #download cfg bin file
                    download_and_unzip(FILESERVER_HOST, config_bin_file_name, TMP_PATH, HUB_CFG_PATH)
                    #print("downloading cfg bin file...")
                    #download(FILESERVER_HOST, config_bin_file_name, TMP_PATH, HUB_CFG_PATH)                    

                print(api_res_json['module']['module_id']+","+api_res_json['module']['prod_code'])    
                #input("Press Enter to continue...")                
                status, step = get_hub_status(api_res_json['module']['module_id'], api_res_json['module']['prod_code'],DEBUG_MODE)
                
                print("status:", status, "step:", step)
                #input("Press Enter to continue...")
                if(status == 'F'):
                    current_step = 0
                    if(step is None or step == ''):
                        current_step = 2
                    else:
                        current_step = int(step) + 1
                    
                    while(current_step <= 6):
                        print("current_step:", current_step)
                        result = flashProc(command_dict, current_step, FIRMWARE_PATH, combined_BinFileName, HUB_CFG_PATH, api_res_json['module']['hub_id'],KEY_FILE_PATH,DEBUG_MODE,COM_PORT)
                        if(result['code'] == '200'):
                            #conn.sendall(json.dumps(result).encode())
                            update_hub_burn_status(api_res_json['module']['module_id'], api_res_json['module']['prod_code'], 'F', current_step)
                            current_step = current_step + 1
                            print("next_step:", current_step)
                            #input("Press Enter to continue...")
                        else:
                            return result                                                                    
                else:
                    return {"cmdAck": "flash", "reqId": command_dict.get("reqId"), "code": "952", "msg": f"Wrong module status {status}, can not flash bin file"}
                #check if bin file flashed
                print("flash bin file completed!")
                url = SERVER_HOST+"completeFlash"
                api_res = requests.post(url, headers=headers, json=data)
                return {"cmdAck": "flash", "reqId": command_dict.get("reqId"), "code": "200", "msg": "Success"}
            elif (api_res_json['module']['status']=='C'):
                return {"cmdAck": "flash", "reqId": command_dict.get("reqId"), "code": "201", "msg": f"Module status {api_res_json['module']['status']}."}
            else:
                return {"cmdAck": "flash", "reqId": command_dict.get("reqId"), "code": "952", "msg": f"Wrong module status {api_res_json['module']['status']}, can not flash bin file"}
        else:
            return {"cmdAck": "flash", "reqId": command_dict.get("reqId"), "code": api_res_json['code'], "msg": api_res_json['message']}
    else:
        return {"cmdAck": "format", "reqId": command_dict.get("reqId"), "code": "909", "msg": "Failed to connect dcms server"}        

def updateBinIntoModule(command_dict, conn):
    if 'modid' not in command_dict:
        return {"cmdAck": "update", "reqId": command_dict.get("reqId"), "code": "910", "msg": "Invalid parameters: modid is required"}

    if 'hubId' not in command_dict:
        return {"cmdAck": "update", "reqId": command_dict.get("reqId"), "code": "910", "msg": "Invalid parameters: hubId is required"}

    if DEBUG_MODE:
        return {"cmdAck": "update", "reqId": command_dict.get("reqId"), "code": "200", "msg": "success"}

    #getHubInfo
    headers = {'token': TOKEN}
    data = {'module_id': command_dict['modid'], 'user_name': AGENT_NAME, 'prod_code': command_dict['prodCode']}
    url = SERVER_HOST+"getFwBinFileName" 

    api_res = requests.post(url, headers=headers, json=data)    
    if api_res.status_code == 200:
        api_res_json = api_res.json()
        print(api_res_json)
        #checkModuleStatus and hubData
        if (api_res_json['code'] == '200'):
                #downloadBin
                fw_bin_file = getFwBinFileName(api_res_json['fw_def']['file'],'')
                if(fw_bin_file is None):
                    return {"cmdAck": "flash", "reqId": command_dict.get("reqId"), "code": "951", "msg": "Failed to get fw bin file name"}

                print(fw_bin_file)
                
                #check if BIN folder exist                
                if not os.path.exists(FIRMWARE_PATH+fw_bin_file['file']):   
                    #download bin file
                    print("downloading bin file...")
                    download(FILESERVER_HOST, fw_bin_file['file'], FIRMWARE_PATH)   
                    #download bin file
                    print("downloaded bin file!")
                
                # input("Press Enter to continue...")
                result = updartProc(command_dict, FIRMWARE_PATH, fw_bin_file['file'],DEBUG_MODE)  
                if(result['code'] != '200'):
                    # input("Press Enter to continue...")
                    return result
                # else:
                                                                                        
                #check if bin file flashed
                print("flash bin file completed!")
                url = SERVER_HOST+"completeFlash"
                api_res = requests.post(url, headers=headers, json=data)
                return {"cmdAck": "flash", "reqId": command_dict.get("reqId"), "code": "200", "msg": "Success"}
        else:
            return {"cmdAck": "flash", "reqId": command_dict.get("reqId"), "code": api_res_json['code'], "msg": api_res_json['message']}
    else:
        return {"cmdAck": "format", "reqId": command_dict.get("reqId"), "code": "909", "msg": "Failed to connect dcms server"}     


def formatModule(command_dict):
    # 0. 校验输入参数
    if 'modid' not in command_dict:
        return {"cmdAck": "format", "reqId": command_dict.get("reqId"), "code": "910", "msg": "Invalid parameters: modid is required"}

    if DEBUG_MODE:
        return {"cmdAck": "format", "reqId": command_dict.get("reqId"), "code": "200", "msg": "success"}
        #return eraseFlashProc01(command_dict)

    # 1. 调用getModuleId获取module status
    headers = {'token': TOKEN}
    data = {'module_id': command_dict['modid'], 'user_name': AGENT_NAME, 'prod_code': ''}
    url = SERVER_HOST+"initModule"
    api_res = requests.post(url, headers=headers, json=data)    
    # 2. 如果module status为0，调用formatModule    
    if api_res.status_code == 200:
        api_res_json = api_res.json()
        print(api_res_json)
        if (api_res_json['code'] == '200'):
            if (api_res_json['module']['status']=='0'):
                print("formatting module...")

                #验证当前的板子是否加密过，若已经加密过再擦除会导致芯片损毁,所以这里验证有加密过则退出当前擦除的步骤。
                result = verifyNotEncyptedBoard(command_dict,COM_PORT,DEBUG_MODE)
                if(result['code'] != '200'):
                    return result
                
                eraseFlashProc01(command_dict)
                print("formatting module completed!")
                url = SERVER_HOST+"completeInit"
                api_res = requests.post(url, headers=headers, json=data)
                print(api_res.status_code)
                for i in range(3):
                    if api_res.status_code == 200:                        
                        api_res_json = api_res.json()
                        if (api_res_json['code'] == '200'):
                            return {"cmdAck": "format", "reqId": command_dict.get("reqId"), "code": "200", "msg": "success"}
                        else:
                            return {"cmdAck": "format", "reqId": command_dict.get("reqId"), "code": api_res_json['code'], "msg": api_res_json['message']}
                    else:
                        print("Failed to update module status, try again... i =", i)
                        time.sleep(2)                        
                        api_res = requests.post(url, headers=headers, json=data)
                        print(api_res.status_code)
                        
                return {"cmdAck": "format", "reqId": command_dict.get("reqId"), "code": "951", "msg": "Formatted module, failed to update module status, need to handel manually"}
            else:
                return {"cmdAck": "format", "reqId": command_dict.get("reqId"), "code": '950', "msg": "wrong module status", "status": api_res_json['module']['status']}
        else:
            return {"cmdAck": "format", "reqId": command_dict.get("reqId"), "code": api_res_json['code'], "msg": api_res_json['message']}
    else:    
        # 3. 否则返回错误信息
        return {"cmdAck": "format", "reqId": command_dict.get("reqId"), "code": "909", "msg": "Failed to connect dcms server"}
    

def DebugBinIntoModule(command_dict, conn):
    if 'modid' not in command_dict:
        return {"cmdAck": "update", "reqId": command_dict.get("reqId"), "code": "910", "msg": "Invalid parameters: modid is required"}

    if 'hubId' not in command_dict:
        return {"cmdAck": "update", "reqId": command_dict.get("reqId"), "code": "910", "msg": "Invalid parameters: hubId is required"}

    if DEBUG_MODE:
        return {"cmdAck": "update", "reqId": command_dict.get("reqId"), "code": "200", "msg": "success"}

    #getHubInfo
    headers = {'token': TOKEN}
    data = {'module_id': command_dict['modid'], 'user_name': AGENT_NAME, 'prod_code': command_dict['prodCode']}
    # url = SERVER_HOST+"getHubInfoByModuleId"
    url = SERVER_HOST+"getFwBinFileName" 

    print("data:\n" + json.dumps(data, indent=2))

    api_res = requests.post(url, headers=headers, json=data)    
    
    api_res_json = api_res.json()

    print("api_res_json:\n" + json.dumps(api_res_json, indent=2))
    # fw_bin_file = "key_PGH260_v050329.bin"
    # print("get fw_bin_file %s"%(fw_bin_file))

    # config_bin_file_name = api_res_json['module']['hub_id'] + ".zip"
    # #download cfg bin file  下载 hub_id
    # download_and_unzip(FILESERVER_HOST, config_bin_file_name, TMP_PATH, HUB_CFG_PATH)

    # input("yuezheng.gao  DebugBinIntoModule  Press Enter to continue...")

    # result = verifyNotEncyptedBoard(command_dict,COM_PORT,DEBUG_MODE)
    # if(result['code'] != '200'):
    #     return result

    sys.exit(0)  # 正常退出

    ########### 烧录单 APP 固件 测试 start########
    # result = updartProc(command_dict, FIRMWARE_PATH, fw_bin_file,DEBUG_MODE)  
    # if(result['code'] != '200'):
    #     print("updartProc error")
    #     # input("Press Enter to continue...")
    #     # return result
    # else:
    #     print("updartProc successfull")
    # return result
    ########### 烧录单 APP 固件 测试 end########


    ########### 合并固件 测试 start########
    # combined_BinFileName = fw_bin_file.replace("key_", "Combine_")  
    # print("key_AppBinFileName = %s need combined_file = %s"%(fw_bin_file,combined_BinFileName))

    # # 这里判断是否有combine bin 文件
    # if not os.path.exists(FIRMWARE_PATH+combined_BinFileName):   
    #     #download bin file
    #     #print("downloading bin file...")
    #     # download(FILESERVER_HOST, fw_bin_file['file'], FIRMWARE_PATH)   

    #     # 下载固件后 合并成 combine bin
    #     result = comboneBinProc(command_dict,FIRMWARE_PATH,fw_bin_file,DEBUG_MODE)
    #     if(result['code'] != '200'):
    #         return result
    #     print("downloaded bin file and combone bin file done!")
    #     print("result = %s"%(result))
            
    #     input("Press Enter to exit...")
    # else:
    #     print("%s already exit" %(FIRMWARE_PATH+fw_bin_file))   
    #     result = {"cmdAck": "combine", "reqId": command_dict.get("reqId"), "code": "200", "msg":"cobime_bin file is already exit"}
    # return result
    ########### 合并固件 测试 end########

def execute_command(cmdReq, command_dict, conn):
    #print(command_dict)
    if command_dict.get("cmdReq") == "ping":        
        response = {"cmdAck": "pong", "reqId": command_dict.get("reqId"),"code":"200","msg":"success"}
    elif command_dict.get("cmdReq") == "espToolStatus":
        response = checkEspToolProc(command_dict, DEBUG_MODE)
    elif command_dict.get("cmdReq") == "getHubInfo":
        # 执行获取hub信息的程序
        command_dict["agentName"] = AGENT_NAME
        response = getHubInfo(command_dict, DEBUG_MODE)
    elif command_dict.get("cmdReq") == "flash":
        # 执行烧录程序
        response = flashBinIntoModule(command_dict, conn)
    elif command_dict.get("cmdReq") == "format":
        response = formatModule(command_dict)
    elif command_dict.get("cmdReq") == "update":
        # 执行仅仅烧录key_APP程序 (覆盖)
        response = updateBinIntoModule(command_dict, conn)
    elif command_dict.get("cmdReq") == "debug":
        # 调试指令
        response = DebugBinIntoModule(command_dict, conn)
    else:        
        response = {"cmdAck": command_dict['cmdReq'], "reqId": command_dict.get("reqId"), "code": "400", "msg": "Invalid command"}
    
    return json.dumps(response)

def start_server(host='0.0.0.0', port=9000):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind((host, port))
        s.listen()
        print(f"Server started on {host}:{port}. Waiting for connection...")

        while True:  # 外部无限循环
            conn, addr = s.accept()
            with conn:
                print(f"Connected by {addr}")
                while True:
                    data = conn.recv(1024).decode()
                    if not data:
                        break
                    try:
                        print(data.strip())
                        command_dict = json.loads(data.strip())                        
                        if not isinstance(command_dict, dict):
                            conn.sendall(json.dumps({"code": "912", "msg": "Invalid JSON data"}).encode())
                        
                        if 'cmdReq' not in command_dict:
                            response = {"code": "910", "msg": "Invalid parameters"}
                            response_json = json.dumps(response)
                            conn.sendall(response_json.encode())
                            continue

                        cmdReq = command_dict.get("cmdReq")
                        if cmdReq == "exit":
                            response = {"code": "990", "msg": "Exiting in a 5 seconds" }                     
                            conn.sendall(json.dumps(response).encode())
                            time.sleep(5)  # Pause the current thread for 5 seconds
                            return  # 退出 start_server 函数，关闭服务器

                        response = execute_command(cmdReq, command_dict, conn)
                        conn.sendall(response.encode())
                    except json.JSONDecodeError:
                        conn.sendall(json.dumps({"code": "912", "msg": "Invalid JSON data"}).encode())


local_ip = get_local_ip()
print("Local IP Address:", local_ip)

start_server()